package com.atguigu.gmall.activity.listener;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.redis.CacheHelper;
import com.atguigu.gmall.activity.utils.DateUtil;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;

/**
 * @author 李旭
 * @date 2021/10/30 15:50
 * @Description: 监听器
 * <p>
 * 由定时器发起的消息   现在由秒杀微服务的MQ的监听器接收
 * １、将DB中数据压入缓存　　放入缓存
 */
@Component
public class ActivityListener {


    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 秒杀
     */
    //监听开始抢购
    @RabbitListener(bindings = {@QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER, durable = "true", autoDelete = "false"),
            key = MqConst.ROUTING_SECKILL_USER
    )})
    public void startSeckill(UserRecode userRecode, Message message, Channel channel) {
        try {
//            1、校验状态位
            Object status = CacheHelper.get(userRecode.getSkuId().toString());
            if(!"1".equals(status)){
                //无货
                //应答  队列 删除此消息  不要再发过来了
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            2、校验用户是否已经买过了      K：前缀 +  userId + ":" +skuId  哪个用户 买哪个商品  3:30 2:31
//            2.1 setnx  分布式缓存那会儿学的
//            setnx(skuId,haha) : 如果此skuID已经存在了 返回值 false 不设置  已经来过了
//            setnx(skuId,haha) : 如果此skuID不存在 返回值 true 设置   没来过了
            Boolean isSale = redisTemplate.opsForValue().setIfAbsent(RedisConst.SECKILL_USER +
                    userRecode.getUserId() + ":" + userRecode.getSkuId(), "");
            if(!isSale){
                //此用户已经来过了 不能再买了
                //应答  队列 删除此消息  不要再发过来了
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            3、获取秒杀商品
            String skuId = (String) redisTemplate.opsForList().rightPop(
                    RedisConst.SECKILL_STOCK_PREFIX
                    + userRecode.getSkuId());
            if(StringUtils.isEmpty(skuId)){
                //3.1没有 抢购失败
                redisTemplate.convertAndSend("seckillpush",
                        userRecode.getSkuId() + ":0");//更新状态位
                //应答
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
//            4、有 抢购成功
            //4.1:从缓存中查询出此秒杀商品信息
            SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.opsForHash().
                    get(RedisConst.SECKILL_GOODS, userRecode.getSkuId().toString());
            //4.2:再保存当前用户的秒杀商品的资格
            redisTemplate.opsForValue().set(RedisConst.SECKILL_ORDERS
                    + userRecode.getUserId() +  ":" + userRecode.getSkuId(),seckillGoods);
            //5:..同步DB   善后工作  不是秒杀核心代码
            //应答
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    //定时器 设置的是 5S执行一次
    @RabbitListener(bindings = {@QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            value = @Queue(value = MqConst.QUEUE_TASK_1, durable = "true", autoDelete = "false"),
            key = MqConst.ROUTING_TASK_1
    )})
    public void findSeckillGoodsToRedis(Message message, Channel channel) {
        try {
            //1:查询DB中的秒杀商品
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            //只查询某些字段  select id,name from 表
            //seckillGoodsQueryWrapper.select("id,name");
            //1.1:秒杀的商品必须是审核通过的
            seckillGoodsQueryWrapper.eq("status", "1");
            //大于
            //1.2:秒杀的商品必须有剩余库存
            seckillGoodsQueryWrapper.gt("stock_count", 0);
            //1.3:秒杀的商品必须是活动范围之内 （ 只秒杀当天的数据 ）
            seckillGoodsQueryWrapper.eq("DATE(start_time)", DateUtil.formatDate(new Date()));
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.
                    selectList(seckillGoodsQueryWrapper);
            //2:将查询的秒杀商品的压入缓存中  Hash类型
            for (SeckillGoods seckillGoods : seckillGoodsList) {
                //2.1:判断将商品是否已经在缓存存在了
                if (!redisTemplate.opsForHash().hasKey(RedisConst.SECKILL_GOODS,
                        seckillGoods.getSkuId().toString())) {
                    //参数1 ： h  Map h =  常量     String类型
                    //参数2 ： hk h.put(hk    hk:库存ID skuId String类型
                    //参数3 ： hv h.put(hk,hv)            HV:任意类型 （POJO或必须实现序列化接口）
                    redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS,
                            seckillGoods.getSkuId().toString(), seckillGoods);
                    //3:防止超卖  将秒杀的商品放到货架上  放商品本身（skuID)  不要放商品的数量 (skuNum)
                    // 放商品本身（skuID) 30
                    // 放商品的数量 (skuNum) 10

                    //参数1： List l = new ARrayList  l
                    //public static final String SECKILL_STOCK_PREFIX = "seckill:stock:";
                    //参数2：  l.add(苹果） 苹果
                    String[] skuIds = buildSkuIdArray(seckillGoods);
                    redisTemplate.opsForList().leftPushAll(RedisConst.SECKILL_STOCK_PREFIX
                            + seckillGoods.getSkuId(), skuIds);
                    //4:状态位  发布、订阅模型
                    redisTemplate.convertAndSend("seckillpush",
                            seckillGoods.getSkuId() + ":1");
                }
            }
            //应答
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //准备商品数量的商品ID   10件商品    10个商品ID的数组
    private String[] buildSkuIdArray(SeckillGoods seckillGoods) {
        Integer stockCount = seckillGoods.getStockCount();
        String[] ids = new String[stockCount];
        for (Integer i = 0; i < stockCount; i++) {
            ids[i] = seckillGoods.getSkuId().toString();
        }
        return ids;

    }
}
